package com.baiqian.doupo.tileentity;

import com.baiqian.doupo.block.lian_yao_ding.HuoDing;
import com.baiqian.doupo.capability.DouPoModCapability;
import com.baiqian.doupo.capability.douqi.IDouQiCapability;
import com.baiqian.doupo.item.DanYao;
import com.baiqian.doupo.item.ItemRegistry;
import com.baiqian.doupo.item.MoHe;
import com.baiqian.doupo.item.YaoCai;
import net.minecraft.block.AbstractFurnaceBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tags.BlockTags;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.LazyOptional;
import org.apache.logging.log4j.LogManager;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static net.minecraft.util.EntityPredicates.IS_ALIVE;

public class HuoDingTileEntity extends TileEntity implements ITickableTileEntity {
    private UUID uuid=null;//玩家uuid
    private double temperature = 21;//丹炉内的温度
    private boolean is_making=false;//是否正在炼丹
    private double quality=100;//丹药品质
    private int failNum=0;//炼化失败的药材数量
    /**炼制中的药材*/
    private List<ItemStack> making_item = new ArrayList<>();
    /**已经炼制完毕的药材*/
    private List<ItemStack> finish_item = new ArrayList<>();
    /**已经炼制的时间*/
    private List<Integer> making_tick = new ArrayList<>();
    /**已经炼制的点数*/
    private List<Double> making_point = new ArrayList<>();
    /**完成过某种丹药的炼制*/
    private boolean is_even_finishing = false;
    /**药材失败过*/
    private boolean is_even_bad = false;
    /**正在起火*/
    private boolean is_burning = false;


    public HuoDingTileEntity() {
        super(TileEntityTypeRegistry.huo_ding_tileentity.get());
    }

    public boolean isEvenFinishing(){
        return is_even_finishing;
    }
    public boolean isEvenBad(){
        return is_even_bad;
    }

    /**增减温度*/
    public void addTemperature(double num){
        temperature+=num;
    }

    /**增减point*/
    public void addMakingPoint(double num){
        for(int i=0;i<making_point.size();++i){
            if(making_tick.get(i)>0){
                making_point.set(i,making_point.get(i)+num);
            }
        }
    }

    /**是否正在炼药*/
    public boolean isMaking(){
        return is_making;
    }

    /**获取丹药品质*/
    public double getQuality(){
        return quality;
    }

    /**获取炼制中的药材*/
    public List<ItemStack> getMakingItem(){
        return making_item;
    }

    /**获取炼制完成的药材*/
    public List<ItemStack> getFinishItem(){
        return finish_item;
    }

    /**是否已经达到起火温度*/
    public boolean isShouldFire(){
        return temperature >= HuoDing.MIN_HOT;
    }

    private final static double fire_after_num_4 = (HuoDing.MAX_HOT-HuoDing.MIN_HOT)/4;
    private final static double fire_before_num_4 = (HuoDing.MIN_HOT-21)/4;
    /**获取当前温度对应的前口图[1,8]*/
    public int getQianKouNum(){
        //起火后
        if(isShouldFire()){
            for(int i=3;i>=0;--i){
                if(temperature >= fire_after_num_4*i+HuoDing.MIN_HOT)
                    return i+4;
            }
        }else{//未起火
            for(int i=3;i>=0;--i){
                if(temperature >= fire_before_num_4*i+21)
                    return i;
            }
        }
        return 0;
    }

    private double getBeActivateTemprature(Item cai_liao){
        if(cai_liao instanceof YaoCai){
            return ((YaoCai)cai_liao).getBeActivateTemprature();
        }else if(cai_liao instanceof MoHe){
            return ((MoHe)cai_liao).getBeActivateTemprature();
        }
        return 10000;
    }
    private double getBeRemoveTemprature(Item cai_liao){
        if(cai_liao instanceof YaoCai){
            return ((YaoCai)cai_liao).getBeRemoveTemprature();
        }else if(cai_liao instanceof MoHe){
            return ((MoHe)cai_liao).getBeRemoveTemprature();
        }
        return 10000;
    }
    private double getBeActivateMaxTick(Item cai_liao){
        if(cai_liao instanceof YaoCai){
            return ((YaoCai)cai_liao).getBeActivateMaxTick();
        }else if(cai_liao instanceof MoHe){
            return ((MoHe)cai_liao).getBeActivateMaxTick();
        }
        return 10000;
    }
    private double getBeActivatePoint(Item cai_liao){
        if(cai_liao instanceof YaoCai){
            return ((YaoCai)cai_liao).getBeActivatePoint();
        }else if(cai_liao instanceof MoHe){
            return ((MoHe)cai_liao).getBeActivatePoint();
        }
        return 10000;
    }
    private boolean isTempratureBigOrEquActivateTemprature(Item cai_liao){
        return temperature >= getBeActivateTemprature(cai_liao);
    }
    private double getTempratureSubActivateTemprature(Item cai_liao){
        return temperature - getBeActivateTemprature(cai_liao);
    }
    private boolean isMakingPointBigOrEquActivatePoint(double making_point,Item cai_liao){
        return making_point >= getBeActivatePoint(cai_liao);
    }
    private boolean isTempratureBigOrEquRemoveTemprature(Item cai_liao){
        return temperature >= getBeRemoveTemprature(cai_liao);
    }
    private boolean isMakingTickBigOrEquMaxTick(int making_tick,Item cai_liao){
        return making_tick >= getBeActivateMaxTick(cai_liao);
    }

    /**获取当前材料对应温度的背景图[0,8]*/
    public int getCaiLiaoTempratureNum(int num){
            Item cai_liao = making_item.get(num).getItem();
            //被炼化温度
            double activate_temprature = getBeActivateTemprature(cai_liao);
            //被销毁温度
            double remove_temprature = getBeRemoveTemprature(cai_liao);
            double one = (remove_temprature-activate_temprature)/8;
            for(int i=8;i>0;--i){
                if(temperature>=i*one+activate_temprature)
                    return i;
            }
        return 0;
    }

    /**获取当前材料对应Point的背景图[0,12]*/
    public int getCaiLiaoPointNum(int num){
        Item cai_liao = making_item.get(num).getItem();
        double one = getBeActivatePoint(cai_liao)/12;
        for(int i=12;i>0;--i){
            if(making_point.get(num)>one*(i-1)){
                return i;
            }
        }
        return 0;
    }

    /**获取当前材料对应时间的背景图[0,12]*/
    public int getCaiLiaoTimeNum(int num){
        Item cai_liao = making_item.get(num).getItem();
        double one = getBeActivateMaxTick(cai_liao)/12;
        for(int i=12;i>0;--i){
            if(making_tick.get(num)>one*(i-1)){
                return 12-i;
            }
        }
        return 12;
    }

    /**获取当前uuid的玩家*/
    public PlayerEntity getPlayer(){
        return world.getPlayerByUuid(uuid);
    }

    /**删除方块*/
    public void remove_block(){
        throwItemBack();
        world.removeBlock(pos,false);
//                world.destroyBlock(pos,false);
    }

    /**将所有物品排出*/
    public void throwItemBack(){
        for(int i=0;i<making_item.size();++i){
            //尚未炼化
            ItemStack itemStack = making_item.get(i).copy();
            itemStack.setCount(1);
            world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), itemStack));
        }
        making_item.clear();
        making_tick.clear();
        if(is_making) {
            for (int i = 0; i < finish_item.size(); ++i) {
                ItemStack itemStack = new ItemStack(ItemRegistry.yao_zha.get());
                itemStack.setCount(1);
                world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), itemStack));
            }
        }
        finish_item.clear();
        for(int i=0;i<failNum;++i){
            if(Math.random()<0.75) {
                ItemStack itemStack = new ItemStack(ItemRegistry.yao_zha.get());
                itemStack.setCount(1);
                world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), itemStack));
            }
        }
    }

    /**将所有物品排出到玩家*/
    public void throwItemBack(PlayerEntity player){
        for(int i=0;i<making_item.size();++i){
            //尚未炼化
            ItemStack itemStack = making_item.get(i).copy();
            itemStack.setCount(1);
            if(!player.addItemStackToInventory(itemStack))
                world.addEntity(new ItemEntity(world, player.getPosX(), player.getPosY(), player.getPosZ(), itemStack));
        }
        making_item.clear();
        making_tick.clear();
        if(is_making) {
            for (int i = 0; i < finish_item.size(); ++i) {
                ItemStack itemStack = new ItemStack(ItemRegistry.yao_zha.get());
                itemStack.setCount(1);
                world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), itemStack));
            }
        }
        finish_item.clear();
        for(int i=0;i<failNum;++i){
            if(Math.random()<0.75) {
                ItemStack itemStack = new ItemStack(ItemRegistry.yao_zha.get());
                itemStack.setCount(1);
                world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), itemStack));
            }
        }
    }

    /**继承的方块删除函数*/
    @Override
    public void remove() {
        if(!world.isRemote) {
            throwItemBack();
        }
        super.remove();
    }

    @Override
    public void tick() {
        boolean flag1=false;
        if (world!=null&&!world.isRemote) {
            //确认玩家还在不在，从而判断是否该维持鼎炉
            if(uuid==null){
                PlayerEntity player = world.getClosestPlayer(pos.getX(), pos.getY(), pos.getZ(), 10, false);
                if(player==null){
                    remove_block();
                    return;
                }
                uuid = player.getUniqueID();
            }
            PlayerEntity player;
            try {
                player = world.getPlayerByUuid(uuid);
            }catch (Exception e){
                LogManager.getLogger().info("HuoDing方块实体获取玩家异常");
                player=null;
                remove_block();
                return;
            }
            if(player==null){//若玩家消失就销毁方块
                remove_block();
                return;
            }
            //维持炉鼎
            LazyOptional<IDouQiCapability> douQiCap = player.getCapability(DouPoModCapability.DOUQI_CAPABILTTY);
            if(douQiCap.isPresent()){
                PlayerEntity finalPlayer = player;
                douQiCap.ifPresent((l)->{
                    double douqi_own = l.getDouQi(0);//获取解压后的斗气
                    douqi_own-=0.001;
                    if(douqi_own<0){
                        remove_block();
                        return;
                    }else{
                        //支付维持费用
                        l.addDouQi(-0.001,0,true, finalPlayer);
                    }
                });
            }
            //获取药材
            tryAddItem();
            //温度变动
            temperatureChange();
            //温度影响
            boolean flag = is_burning;
            is_burning=isShouldFire();
            if (flag != is_burning) {
                flag1 = true;
                this.world.setBlockState(this.pos, this.world.getBlockState(this.pos).with(AbstractFurnaceBlock.LIT, is_burning), 3);
            }
            if(temperature>=HuoDing.MAX_HOT){//如果超过了炼药鼎可承受的最高温度
                remove_block();
                //爆炸
                this.world.createExplosion(null, pos.getX(),pos.getY(),pos.getZ(), 4.0F, Explosion.Mode.BREAK);
            return;
            }
            if(is_making){//已经开始炼药
                if(temperature<HuoDing.MIN_HOT){//低于最低温度
                    throwItemBack();
                    stopMaking();//停止炼药
                }else{
                    continueMaking();//继续炼药
                }
            }
            else{//如果还没开始炼药
                if(temperature>=HuoDing.MIN_HOT){//如果达到了炼药温度
                    if(making_tick.size()>0){//如果存在药材
                        startMaking();//开始炼药
                    }
                }
            }
            world.notifyBlockUpdate(pos, getBlockState(), getBlockState(), Constants.BlockFlags.BLOCK_UPDATE);
        }

        if (flag1) {
            this.markDirty();
        }
    }

    /**尝试从周围环境中吸取药材*/
    private void tryAddItem() {
        //可以加入新药材
        if(making_item.size()<HuoDing.MAX_NUM){
            int can_put_num = HuoDing.MAX_NUM-making_item.size();
            //搜索被扔进方块周围的物品
//                        AxisAlignedBB range = new AxisAlignedBB(pos.getX()-0, pos.getY() - 0,  pos.getZ()-0,
//                                pos.getX() +1, pos.getY() + 1,  pos.getZ()+1);//表示x，y，z所在的这一个格子
            AxisAlignedBB range = new AxisAlignedBB(pos.getX()-1, pos.getY() - 0,  pos.getZ()-1,
                    pos.getX() +2, pos.getY() + 2,  pos.getZ()+2);
            List<ItemEntity> items=world.getEntitiesWithinAABB(ItemEntity.class,range, IS_ALIVE);
            for (ItemEntity item:items){
                ItemStack itemStack = item.getItem();
                //如果是药材或魔核
                if(itemStack.getItem()instanceof YaoCai||itemStack.getItem() instanceof MoHe){
                    //如果物品的数量少于或等于可放入的数量，那就全部放入
                    if(itemStack.getCount()<=can_put_num){
                        for(int i=0;i<itemStack.getCount();++i)
                            addItemStack(itemStack);
                        item.remove();
                    }else{//物品数量多于可放入数量，那就放入一部分
                        for(int i=0;i<can_put_num;++i)
                            addItemStack(itemStack);
                        itemStack.setCount(itemStack.getCount()-can_put_num);
                    }
                }
            }
        }
    }

    /**增加物品进物品列表*/
    private void addItemStack(ItemStack itemStack) {
        making_item.add(itemStack.copy());
        making_tick.add(0);
        making_point.add(0.0);
    }

    /**继续炼化*/
    private void continueMaking() {
        if(is_even_finishing)
            is_even_finishing=false;
        if(is_even_bad)
            is_even_bad=false;
        for(int i = 0;i<making_item.size();++i){
            Item cai_liao = making_item.get(i).getItem();
            if(isTempratureBigOrEquActivateTemprature(cai_liao)){
                //正在被炼化
                //获取炼药温差
                double speed = getTempratureSubActivateTemprature(cai_liao);
                //获取每tick增加的炼药点数
                speed/=100;
                ++speed;
                //增加炼药点数
                making_point.set(i,making_point.get(i)+speed);
                //如果炼药点数达标就炼化成功
                if(isMakingPointBigOrEquActivatePoint(making_point.get(i),cai_liao)){
                    finish_item.add(making_item.get(i).copy());
                    making_item.remove(i);
                    making_tick.remove(i);
                    making_point.remove(i);

                    //如果已经可以成就某个丹药，就弹出个特效
                    ItemStack danYaoStack = DanYao.getDanYao(finish_item,quality);
                    if(danYaoStack!=null) {
                        is_even_finishing = true;
                    }
                    --i;
                    continue;
                }
                //时间经过1tick
                making_tick.set(i,making_tick.get(i)+1);
            }//如果正在炼制中低于炼制温度则过冷被销毁
            else if(making_tick.get(i)>0){
                is_even_bad=true;
                making_item.remove(i);
                making_tick.remove(i);
                making_point.remove(i);
                quality-=5;
                --i;
                continue;
            }
            if(isTempratureBigOrEquRemoveTemprature(cai_liao)){//高温被销毁
                is_even_bad=true;
                making_item.remove(i);
                making_tick.remove(i);
                making_point.remove(i);
                quality-=10;
                --i;
                continue;
            }
                if(isMakingTickBigOrEquMaxTick(making_tick.get(i),cai_liao)){//超时被销毁
                    is_even_bad=true;
                making_item.remove(i);
                making_tick.remove(i);
                making_point.remove(i);
                quality-=20;
                --i;
                continue;
            }
        }
        //品质亏损(每分钟亏损60)
        quality-=0.05;
        //炼丹失败
        if(quality<0) {
            throwItemBack();
            stopMaking();
        }
    }

    private void startMaking() {
        is_making = true;
        quality=100;//丹药品质
        failNum=0;//炼化失败的药材数量
    }

    public void stopMaking() {
        is_making = false;
    }

    private void temperatureChange() {
        //岩浆或者火焰或营火加热
        Block block = world.getBlockState(new BlockPos(pos.getX(),pos.getY()-1,pos.getZ())).getBlock();
        if(BlockTags.FIRE.contains(block)||BlockTags.CAMPFIRES.contains(block)){
            if(temperature<500)
                temperature+=0.13888;
        }else if(Fluids.LAVA.getDefaultState().getBlockState().matchesBlock(block)){
            if(temperature<500)
                temperature+=0.41666;
        }
        //炼丹炉开始散温
        if(temperature> HuoDing.BASE_HOT){
            //与常温拉的越开降温越快
            temperature-=(temperature-HuoDing.BASE_HOT)*0.0002;//1s降低0.958°*2
            if(temperature<HuoDing.BASE_HOT)
                temperature=HuoDing.BASE_HOT;
        }else if(temperature<HuoDing.BASE_HOT){
            //炼丹炉开始回温
            temperature+=(HuoDing.BASE_HOT-temperature)*0.0002;//1s回升0.958°*2
            if(temperature>HuoDing.BASE_HOT)
                temperature=HuoDing.BASE_HOT;
        }
    }

    @Nullable
    @Override//服务端发送数据包用的方法
    public SUpdateTileEntityPacket getUpdatePacket() {
        return new SUpdateTileEntityPacket(pos, 1/*序列号值，可以随便填写*/, getUpdateTag());
    }

    @Override//客户端接受数据包的方法
    public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
        handleUpdateTag(world.getBlockState(pkt.getPos()), pkt.getNbtCompound());
    }

    //在区块刚被载入时被调用的方法:打包
    @Override
    public CompoundNBT getUpdateTag() {
        CompoundNBT compoundNBT = super.getUpdateTag();
        if(uuid==null){
            PlayerEntity player = world.getClosestPlayer(pos.getX(), pos.getY(), pos.getZ(), 10, false);
            uuid = player.getUniqueID();
        }
        compoundNBT.putUniqueId("uuid", uuid);
        compoundNBT.putDouble("temperature",temperature);
        compoundNBT.putBoolean("is_making",is_making);
        compoundNBT.putDouble("quality",quality);
        compoundNBT.putInt("failNum",failNum);
        YaoCai.getNbtFromList(making_item,compoundNBT,1,making_tick,making_point);
        MoHe.getNbtFromList(making_item,compoundNBT,1,making_tick,making_point);
        YaoCai.getNbtFromList(finish_item,compoundNBT,2,making_tick,making_point);
        MoHe.getNbtFromList(finish_item,compoundNBT,2,making_tick,making_point);
        compoundNBT.putBoolean("is_even_finishing",is_even_finishing);
        compoundNBT.putBoolean("is_even_bad",is_even_bad);
        compoundNBT.putBoolean("is_burning",is_burning);
        return compoundNBT;
    }

    //在区块刚被载入时被调用的方法：解包
    @Override
    public void handleUpdateTag(BlockState state, CompoundNBT tag) {
        uuid = tag.getUniqueId("uuid");
        temperature=tag.getDouble("temperature");
        is_making=tag.getBoolean("is_making");
        quality=tag.getDouble("quality");
        failNum=tag.getInt("failNum");
        List<Integer> making_tick_temp = new ArrayList<>();
        List<Double> making_point_temp = new ArrayList<>();
        making_item=YaoCai.getListFromNbt(tag,1,making_tick_temp,making_point_temp);
        making_item.addAll(MoHe.getListFromNbt(tag,1,making_tick_temp,making_point_temp));
        making_tick=making_tick_temp;
        making_point=making_point_temp;
        finish_item=YaoCai.getListFromNbt(tag,2,making_tick_temp,making_point_temp);
        finish_item.addAll(MoHe.getListFromNbt(tag,2,making_tick_temp,making_point_temp));
        is_even_finishing=tag.getBoolean("is_even_finishing");
        is_even_bad=tag.getBoolean("is_even_bad");
        is_burning=tag.getBoolean("is_burning");
    }


    @Override
    public void read(BlockState state, CompoundNBT nbt) {
        super.read(state, nbt);
        handleUpdateTag(state,nbt);
    }

    @Override
    public CompoundNBT write(CompoundNBT compound) {
        super.write(compound);
        return getUpdateTag();
    }
}
